﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;

namespace Code.Commen
{
    [Serializable]
    public class HashTable<K, V> : IEnumerable<KeyValue<K, V>>, ICollection<KeyValue<K, V>>
    {
        private Hashtable _table;
        private ReadWriteObjectLock _lock;

        public HashTable()
        {
            _table = new Hashtable();
            _lock = new ThreadStateLock();
        }

        public V this[K key]
        {
            get { return this.Get(key); }
            set { this.Add(key, value); }
        }

        public bool ContainsKey(K key)
        {
            this._lock.AcquireWrite();
            try
            {
                return _table.ContainsKey(key);
            }
            finally
            {
                this._lock.ReleaseWrite();
            }
            
        }

        public bool ContainsValue(V val)
        {
            this._lock.AcquireWrite();
            try
            {
                return _table.ContainsValue(val);
            }
            finally
            {
                this._lock.ReleaseWrite();
            }
            
        }

        public void Remove(K key)
        {
            _table.Remove(key);
        }

        public void Add(KeyValue<K, V> item)
        {
            _table.Add(item.Key, item.Value);
        }
        public void Add(K key, V val)
        {
            this._lock.AcquireWrite();
            try
            {
                if (!_table.ContainsKey(key))
                    _table.Add(key, val);
                else
                    _table[key] = val;
            }
            finally
            {
                this._lock.ReleaseWrite();
            }
        }
        public V Get(K key)
        {

            this._lock.AcquireRead();
            try
            {
                return (V)_table[key];
            }
            finally
            {
                this._lock.ReleaseRead();
            }
        }

        public void Clear()
        {

            this._lock.AcquireWrite();
            try
            {
                _table.Clear();
            }
            finally
            {
                this._lock.ReleaseWrite();
            }
        }

        public bool Contains(KeyValue<K, V> item)
        {

            this._lock.AcquireRead();
            try
            {
                return _table.Contains(item.Key);
            }
            finally
            {
                this._lock.ReleaseWrite();
            }

        }

        public void CopyTo(KeyValue<K, V>[] array, int arrayIndex)
        {
            this._lock.AcquireRead();
            try
            {
                if (array == null)
                {
                    throw new ArgumentNullException("array");
                }
                if (array.Rank != 1)
                {
                    throw new ArgumentException();
                }
                if (arrayIndex < 0)
                {
                    throw new ArgumentOutOfRangeException("arrayIndex");
                }
                if ((array.Length - arrayIndex) < this.Count)
                {
                    throw new ArgumentException();
                }
                var k = this.Keys.ToArray();
                int length = k.Length;
                while (--length >= 0)
                {
                    K key = k[length];
                    if (key != null)
                    {
                        var entry = new KeyValue<K, V>(key, this[key]);
                        array.SetValue(entry, arrayIndex++);
                    }
                }
            }
            finally
            {
                this._lock.ReleaseRead();
            }
        }

        public bool IsReadOnly
        {
            get { return _table.IsReadOnly; }
        }

        public bool Remove(KeyValue<K, V> item)
        {

            this._lock.AcquireWrite();
            try
            {
                bool rs = this.Contains(item);
                _table.Remove(item.Key);
                return rs;
            }
            finally
            {
                this._lock.ReleaseWrite();
            }
        }
        public int Count { get { return _table.Count; } }
        public IEnumerable<K> Keys { get { return _table.Keys.Cast<K>(); } }
        public IEnumerable<V> Values { get { return _table.Values.Cast<V>(); } }

        public IEnumerator GetEnumerator()
        {
            this._lock.AcquireRead();
            try
            {
                return _table.GetEnumerator();
            }
            finally
            {
                this._lock.ReleaseRead();
            }
        }

        IEnumerator<KeyValue<K, V>> IEnumerable<KeyValue<K, V>>.GetEnumerator()
        {
            return new HashtableEnumerator(this._table);
        }

        [Serializable]
        private class HashtableEnumerator : IEnumerator<KeyValue<K, V>>
        {
            private IDictionaryEnumerator enumerator;
            private Hashtable table;

            // Methods
            internal HashtableEnumerator(Hashtable hashtable)
            {
                this.table = hashtable;
                this.enumerator = hashtable.GetEnumerator();
            }
            public bool MoveNext()
            {
                return enumerator.MoveNext();
            }

            public KeyValue<K, V> Current
            {
                get
                {
                    return new KeyValue<K, V>((K)this.enumerator.Key, (V)this.enumerator.Value);
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    return new KeyValue<K, V>((K)this.enumerator.Key, (V)this.enumerator.Value);
                }
            }
            void IEnumerator.Reset()
            {
                enumerator.Reset();
            }

            KeyValue<K, V> IEnumerator<KeyValue<K, V>>.Current
            {
                get { return new KeyValue<K, V>((K)this.enumerator.Key, (V)this.enumerator.Value); }
            }

            public void Dispose()
            {
            }

        }
    }

    public class KeyValue<K, V>
    {
        public KeyValue(K key, V val)
        {
            this.Key = key;
            this.Value = val;
        }
        public K Key { get; private set; }
        public V Value { get; private set; }
    }

    internal class ReadWriteObjectLock
    {
        // Fields
        private int _lock;

        // Methods
        internal ReadWriteObjectLock()
        {
        }

        internal virtual void AcquireRead()
        {
            lock (this)
            {
                while (this._lock == -1)
                {
                    try
                    {
                        Monitor.Wait(this);
                        continue;
                    }
                    catch (ThreadInterruptedException)
                    {
                        continue;
                    }
                }
                this._lock++;
            }
        }

        internal virtual void AcquireWrite()
        {
            lock (this)
            {
                while (this._lock != 0)
                {
                    try
                    {
                        Monitor.Wait(this);
                        continue;
                    }
                    catch (ThreadInterruptedException)
                    {
                        continue;
                    }
                }
                this._lock = -1;
            }
        }

        internal virtual void ReleaseRead()
        {
            lock (this)
            {
                this._lock--;
                if (this._lock == 0)
                {
                    Monitor.PulseAll(this);
                }
            }
        }

        internal virtual void ReleaseWrite()
        {
            lock (this)
            {
                this._lock = 0;
                Monitor.PulseAll(this);
            }
        }
    }

    internal class ThreadStateLock : ReadWriteObjectLock
    {
        // Fields
        private int _recursionCount;
        private int _threadId;

        private int GetCurrentThreadId()
        {
            return Thread.CurrentThread.ManagedThreadId;
        }

        // Methods
        internal ThreadStateLock()
        {
        }

        internal override void AcquireRead()
        {
            int currentThreadId = this.GetCurrentThreadId();
            if (this._threadId != currentThreadId)
            {
                base.AcquireRead();
            }
        }

        internal override void AcquireWrite()
        {
            int currentThreadId = this.GetCurrentThreadId();
            if (this._threadId == currentThreadId)
            {
                this._recursionCount++;
            }
            else
            {
                base.AcquireWrite();
                this._threadId = currentThreadId;
                this._recursionCount = 1;
            }
        }

        internal void EnsureReleaseWrite()
        {
            int currentThreadId = this.GetCurrentThreadId();
            if (this._threadId == currentThreadId)
            {
                this._threadId = 0;
                this._recursionCount = 0;
                base.ReleaseWrite();
            }
        }

        internal override void ReleaseRead()
        {
            int currentThreadId = this.GetCurrentThreadId();
            if (this._threadId != currentThreadId)
            {
                base.ReleaseRead();
            }
        }

        internal override void ReleaseWrite()
        {
            int currentThreadId = this.GetCurrentThreadId();
            if ((this._threadId == currentThreadId) && (--this._recursionCount == 0))
            {
                this._threadId = 0;
                base.ReleaseWrite();
            }
        }
    }


}
